import jsonpickle

from Bases import BaseObject, setID
from Bases import OSCBaseObject
from DeviceSystem.Groups.Group import GroupBase

class Stack(OSCBaseObject):
    _saved_class_name = 'Stack'
    _saved_attributes = ['id', 'name']
    _saved_child_objects = ['Snapshots']
    _Properties = {'name':dict(type=str), 'current_index':dict(type=int)}
    _ChildGroups = {'Snapshots':{}}
    def __init__(self, **kwargs):
        self.playback = None
        self.MainController = kwargs.get('MainController')
        self.Effects = self.MainController.EffectsSystem
        
        super(Stack, self).__init__(**kwargs)
        self.register_signal('current_index_changed', 'new_snapshot', 'sequencer_state')
        if 'deserialize' not in kwargs:
            self.id = setID(kwargs.get('id'))
            self.name = kwargs.get('name', '')
            #self.Snapshots = {}
#        self.snapshots_indexed = {}
#        for s in self.Snapshots.itervalues():
#            self.snapshots_indexed.update({s.index:s})
#        if len(self.snapshots_indexed):
#            self.current_index = min(self.snapshots_indexed.keys())
#        else:
#            self.current_index = 0
        #self.Snapshots = self.add_ChildGroup(name='Snapshots')
        self.bind(current_index=self.on_current_index_set)
        self.add_osc_handler(callbacks={'child-added':self.on_osc_child_added})
    
    def unlink(self):
        for sn in self.Snapshots.itervalues():
            sn.unlink()
        super(Stack, self).unlink()
        
    def reload_member_obj(self):
        for sn in self.Snapshots.itervalues():
            sn.reload_member_obj()
        
    def on_current_index_set(self, **kwargs):
        value = kwargs.get('value')
        self.next_index = self.find_next_index(value)
        self.emit('current_index_changed', value=value, next=self.next_index)
            
    def set_playback(self, pb):
        self.playback = pb
        for sn in self.Snapshots.itervalues():
            sn.set_playback(pb)
    
    def find_next_index(self, value):
        keys = self.Snapshots.indexed_items.keys()
        if len(keys) == 0:
            return 1
        keys.sort()
        i = keys.index(value)
        if len(keys) <= i + 1:
            return keys[i] + 1
        else:
            return keys[i + 1]
            
    def store_snapshot(self, **kwargs):
        objects = kwargs.get('objects', {})
        index = int(kwargs.get('index'))
        if 'objects' in kwargs:
            del kwargs['objects']
        snapshot = self.Snapshots.indexed_items.get(index)
        if not snapshot:
            kwargs.update({'MainController':self.MainController, 
                           'playback':self.playback})
            if index is not None:
                kwargs['Index'] = index
            snapshot = self.Snapshots.add_child(Snapshot, **kwargs)
            #snapshot.connect('index_changed', self.on_snapshot_index_changed)
            snapshot.connect('active_changed', self.on_snapshot_active_changed)
            #self.Snapshots.update({snapshot.id:snapshot})
            #self.update_snapshot_index()
            self.emit('new_snapshot', obj=snapshot, id=snapshot.id)
            s = snapshot.to_json(indent=None)
            self.osc_node.send_message(address='child-added', value=['Snapshots', s])
        snapshot.add_member(**objects)
        snapshot.store_member_values(send_osc_update=True)
        
    def recall_snapshot(self, **kwargs):
        snapshot = kwargs.get('snapshot')
        id = kwargs.get('id')
        index = kwargs.get('index')
        if not snapshot:
            if id:
                snapshot = self.Snapshots[id]
            elif index:
                snapshot = self.Snapshots.indexed_items.get(index)
        #self.current_index = index
        #snapshot.recall_member_values()
        if snapshot:
            snapshot.active = True
        
#    def update_snapshot_index(self):
#        for key, val in self.Snapshots.iteritems():
#            self.snapshots_indexed.update({val.index:val})
            
    def on_snapshot_active_changed(self, **kwargs):
        if kwargs.get('state'):
            snapshot = kwargs.get('snapshot')
            self.current_index = snapshot.Index
            for sn in self.Snapshots.itervalues():
                if sn != snapshot:
                    sn.active = False
            
#    def on_snapshot_index_changed(self, **kwargs):
#        self.update_snapshot_index()
        
    def on_snapshot_sequencer_state(self, **kwargs):
        sn = kwargs.get('snapshot')
        state = kwargs.get('state')
        self.emit('sequencer_state', snapshot=sn, state=state)
    
    def on_osc_child_added(self, **kwargs):
        values = kwargs.get('values')
        key = values[0]
        d = jsonpickle.decode(values[1])
        if d['attrs']['id'] not in getattr(self, key):
            s = jsonpickle.encode({'saved_children':{key:{d['attrs']['id']:d}}})
            self.from_json(s, saved_child_objects=[key])
            obj = getattr(self, key)[d['attrs']['id']]
            self.emit('new_snapshot', obj=obj, id=obj.id)
        
    def _deserialize_child(self, d):
        if d['saved_class_name'] == 'Snapshot':
            snapshot = self.Snapshots.add_child(Snapshot, MainController=self.MainController, 
                                                deserialize=d, playback=self.playback)
            #snapshot.connect('index_changed', self.on_snapshot_index_changed)
            snapshot.connect('active_changed', self.on_snapshot_active_changed)
            return snapshot

class Snapshot(GroupBase):
    _saved_class_name = 'Snapshot'
    _saved_attributes = ['root_members', 'member_values', 'fade_time']
    signals_to_register = ['fade_time_changed', 'index_changed', 'active_changed', 'sequencer_state']
    def __init__(self, **kwargs):
        self.MainController = kwargs.get('MainController')
        self.Effects = self.MainController.EffectsSystem
        self._fade_time = None
        #self._index = None
        self._active = False
        self.Devices = {}
        self.recalled_by_osc = False
        kwargs.setdefault('osc_address', kwargs.get('Index'))
        super(Snapshot, self).__init__(**kwargs)
        self.fader = self.Effects.add_fader(id=self.id)
        self.fader.connect('sequencer_state', self.on_sequencer_state)
        self.set_playback(kwargs.get('playback'))
        if 'deserialize' not in kwargs:
            self.id = setID(kwargs.get('id'))
            #self.index = kwargs.get('index')
            self.name = kwargs.get('name', str(self.Index))
            self.root_members = set()
            self.member_values = {}
            self.fade_time = kwargs.get('fade_time', 2)
            members = self._get_root_obj(self.root_members)
            self.add_member(**members)
        self.add_osc_handler(callbacks={'set-state':self.on_osc_set_state, 
                                        'member-update':self.on_osc_member_update, 
                                        'store-current':self.on_osc_store_current})
        
    def unlink(self):
        self.fader.disconnect(callback=self.on_sequencer_state)
        self.Effects.del_fader(fader=self.fader)
        super(Snapshot, self).unlink()
        
    def reload_member_obj(self):
        members = self._get_root_obj(self.root_members)
        members.update({'reload_values':True})
        self.add_member(**members)
    
    def _get_root_obj(self, keys):
        members = {}
        for key in keys:
            for group in self.MainController.Groups.itervalues():
                obj = group.search_for_obj(key)
                if obj:
                    break
            if obj:
                members.update({key:obj})
            else:
                print 'key not found: ', key
        return members
        
    @property
    def fade_time(self):
        return self._fade_time
    @fade_time.setter
    def fade_time(self, value):
        value = float(value)
        if value != self._fade_time:
            self._fade_time = value
            self.emit('fade_time_changed', value=value, snapshot=self)
            
#    @property
#    def index(self):
#        return self._index
#    @index.setter
#    def index(self, value):
#        if value != self._index:
#            self._index = value
#            self.emit('index_changed', value=value)
        
    @property
    def active(self):
        return self._active
    @active.setter
    def active(self, state):
        if state != self.active:
            self._active = state
            osc_kwargs = dict(address='set-state', value=state)
            if state:
                if self.osc_node.oscMaster:
                    self.recall_member_values()
                else:
                    osc_kwargs['to_master'] = True
                if not self.recalled_by_osc:
                    self.osc_node.send_message(**osc_kwargs)
            self.emit('active_changed', snapshot=self, state=state)
        self.recalled_by_osc = False
            
    def set_playback(self, pb):
        print self, pb
        self.playback = pb
        
    def add_member(self, **kwargs):
        '''
        :Parameters:
            reload_values : bool, if True, reload member values from dictionary,
                otherwise, use current.  (default is False)
        '''
        if not self.membership_lock:
            reload = kwargs.get('reload_values', False)
            if 'reload_values' in kwargs:
                del kwargs['reload_values']
            for key, val in kwargs.iteritems():
                #self.root_members.add(key)
                if getattr(val, 'base_class') == 'Group' and key not in self.members:
                    d = val.get_objects(captured_only=True)
                    #print 'd: ', d
                    for typekey in ['AttributeGroup']:#, 'Attribute']:
                        for obj in d[typekey].itervalues():
                            super(Snapshot, self).add_member(**{obj.id:obj})
                            self.fader.add_obj(obj)
                            if reload:
                                self.reload_member_values(obj.id)
                            else:
                                self.store_member_values(obj.id)
            if set(kwargs.keys()) != self.root_members:
                self.root_members |= set(kwargs.keys())
                self.osc_node.send_message(address='member-update', value=list(self.root_members))
            
    def del_member(self, key):
        super(Snapshot, self).del_member(key)
        self.fader.del_obj(key)
        if key in self.member_values:
            del self.member_values[key]
        
    
    def reload_member_values(self, key=None):
        if key is not None and key in self.member_values:
            values = {key:self.member_values[key]}
        else:
            values = self.member_values
        for key, val in values.iteritems():
            self.fader.effects['Fade'].store_value(objkey=key, timeV=1.0, value=val)
    
    def store_member_values(self, key=None, send_osc_update=False):
        if key:
            members = {key:self.members[key]}
        else:
            members = self.members
        for key, member in members.iteritems():
            attr = getattr(member, 'value_attribute', 'value')
            self.member_values.update({key:getattr(member, attr)})
            self.fader.effects['Fade'].store_value(objkey=key, timeV=1.0)
        if send_osc_update:
            self.osc_node.send_message(address='store-current')
    
    def recall_member_values(self):
        self.fader.effects['Fade'].store_value(timeV=0.0)
        self.fader.sequencer.duration = self.fade_time
        self.fader.sequencer.start()
    
    def on_osc_set_state(self, **kwargs):
        state = kwargs.get('values')[0]
        if state:
            self.recalled_by_osc = True
            self.active = state
        print 'osc_set_state', self.Index, state
        
    def on_osc_member_update(self, **kwargs):
        value = kwargs.get('values')
        if set(value) != self.root_members:
            self.root_members |= set(value)
            members = self._get_root_obj(value)
            self.add_member(**members)
        
    def on_osc_store_current(self, **kwargs):
        self.store_member_values()
        
    def on_sequencer_state(self, **kwargs):
        state = kwargs.get('state')
        self.emit('sequencer_state', snapshot=self, state=state)